#!/usr/bin/env python3

import os
import stat
import time
import logging
import logging.config
from functools import wraps
import logging.handlers as handlers
from common.benchmark_var import GlobalVar, GlobalEnv

LOG_FORMAT = "%(asctime)s %(filename)23s: %(lineno)-4d %(levelname)-7s %(message)s"
DEFAULT_LEVEL = logging.INFO
DEBUG_LEVEL = logging.DEBUG
MAX_LOGFILE_SIZE = 1024 * 1024 * 1024
LOG_NAME_PREFIX = "GCBS_"
LOG_NAME_SUFFIX = ".log"


def singleton(cls):
    instances = {}

    @wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance


@singleton
class Log:
    """
    Log class, Provides basic log generation and output functions.
    """

    def __init__(self):
        self.log_name = self.get_log_name()
        self._log_path = os.path.join(GlobalEnv.GCBS_TMP_DIR, self.log_name)

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

    def init_log(self, log_level):
        logger = logging.getLogger(self.log_name)
        logger.setLevel(log_level)
        if not logger.handlers:
            logger = self.add_logger_handlers(logger)
        return logger

    def add_logger_handlers(self, logger):
        """
        Output logs on terminals and files.
        :return:
        """
        file_handler = self.set_log_file_handler()
        console_handler = self.set_log_console_handler()

        self.del_handlers(logger)
        self.add_handlers(logger, [file_handler, console_handler])
        self.set_log_file_permission()
        return logger

    @staticmethod
    def get_log_name():
        if not GlobalVar.LOG_NAME:
            time_str = time.strftime("%Y%m%d%H%M%S", time.localtime())
            log_name_list = [LOG_NAME_PREFIX, time_str, LOG_NAME_SUFFIX]
            GlobalVar.LOG_NAME = "".join(log_name_list)
        return GlobalVar.LOG_NAME

    def set_log_level(self, debug):
        """
        set the log level.
        """
        if debug is not None:
            if int(debug):
                self.init_log(DEBUG_LEVEL)
            else:
                self.init_log(DEFAULT_LEVEL)

    def set_log_file_handler(self):
        # When the size of a log file exceeds the upper limit, a new log is generated.
        handler = handlers.RotatingFileHandler(
            self._log_path, maxBytes=MAX_LOGFILE_SIZE, backupCount=3)
        formatter = logging.Formatter(LOG_FORMAT)
        # Setting the log file format:
        # data filename line level information.
        handler.setFormatter(formatter)
        return handler

    def set_log_file_permission(self):
        os.chmod(self._log_path, stat.S_IREAD + stat.S_IWRITE)

    @staticmethod
    def set_log_console_handler():
        return logging.StreamHandler()

    @staticmethod
    def add_handlers(logger, handler_list):
        if not logger.handlers:
            for handler in handler_list:
                logger.addHandler(handler)

    @staticmethod
    def del_handlers(logger):
        while logger.handlers:
            logger.handlers[0].close()
            logger.removeHandler(logger.handlers[0])

    @property
    def log_path(self):
        return self._log_path

    @log_path.setter
    def log_path(self, log_path):
        self.log_name = self.get_log_name()
        self._log_path = os.path.join(log_path, self.log_name)


# If you need to use the log module, you only need to import logger.
log = Log().init_log(DEFAULT_LEVEL)
